package personal.web.mvc.aspect;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Properties;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import personal.exception.UnauthorizedAccessException;
import personal.web.mvc.io.accounting.AccountingRequestParam;


import com.restfb.exception.FacebookOAuthException;
import com.restfb.json.JsonObject;

@Aspect
@Component
public class FacebookAuthorizationAspect {
	private Properties configProperties;

	public Properties getConfigProperties() {
		return configProperties;
	}

	@Autowired(required=true)
	public void setConfigProperties(Properties configProperties) {
		this.configProperties = configProperties;
	}

	/**
	 * authorizes a request
	 * @param code String facebook client auth code
	 * @throws UnauthorizedAccessException
	 */
	/** @Before("execution(* accounting.web.mvc.controller.*.*(..)) && args(param,..)") **/
	public void authorizeRequest(JoinPoint joinPoint, AccountingRequestParam param) throws UnauthorizedAccessException {
		String response = null;
		try {
			URL authUrl = new URL(getFacebookAuthUrl());
			response = getFacebookResponse(authUrl);
			URL graphUrl = new URL(getFacebookGraphUrl(param.getFbCode()));
			response = getFacebookResponse(graphUrl);
		} catch (MalformedURLException e) {
			throw new RuntimeException("Could not create Facebook graph url",e);
		} catch (IOException e) {
			throw new RuntimeException("Could not read Facebook response",e);
		}
		if (!isFacebookAuthorized(response)) {
			FacebookOAuthException exception = getFacebookError(response);
			if (exception != null)
				throw new UnauthorizedAccessException(param.getFbCode(), joinPoint.toShortString(), "Facebook error: " + exception.getMessage());
			else
				throw new UnauthorizedAccessException(param.getFbCode(), joinPoint.toShortString());
		}
	}
	
	private FacebookOAuthException getFacebookError(String response) {
		FacebookOAuthException exception = null;
			JsonObject json = new JsonObject(response);
			if (json != null && json.has("error")) {
	    	  json = json.getJsonObject("error");
	    	  exception = new FacebookOAuthException(json.getString("type"),json.getString("message"));		     
			}
		return exception;
	}
	
	private boolean isFacebookAuthorized(String facebookResponse) {
		String accessToken = null;
		String expires = null;
		String[] pairs = facebookResponse.split("&");
		for(String pair: pairs) {
			String kv[] = pair.split("=");
			if (kv.length != 2) {
				return false;
			}
			if (kv[0].equals("access_token")) {
				accessToken = kv[1];
			}
			if (kv[0].equals("expires")) {
				expires = kv[1];
			}
		}
		if (accessToken == null || expires == null) {
			return false;
		}
		return true;
	}
	
	private String getFacebookResponse(URL url) throws IOException {
		ByteArrayOutputStream baos = new ByteArrayOutputStream();
		HttpURLConnection httpConn = (HttpURLConnection)url.openConnection();
		InputStream is;
		try {
			is = httpConn.getInputStream();
		} catch (IOException e) {
			is = httpConn.getErrorStream();
		}
		int r;
		while((r = is.read()) != -1) {
			baos.write(r);
		}
		return new String(baos.toByteArray());
	}
	
	private String getFacebookAuthUrl() {
		String url = configProperties.getProperty("facebook.auth_url");
		url = url.replace("FB_APP_ID", configProperties.getProperty("facebook.app_id"));
		url = url.replace("FB_REDIRECT_URI", configProperties.getProperty("facebook.redirect_uri"));
		return url;
	}
	
	private String getFacebookGraphUrl(String code) {
		String url = configProperties.getProperty("facebook.graph_url");
		url = url.replace("FB_APP_ID", configProperties.getProperty("facebook.app_id"));
		url = url.replace("FB_APP_SECRET", configProperties.getProperty("facebook.app_secret"));
		url = url.replace("FB_REDIRECT_URI", configProperties.getProperty("facebook.redirect_uri"));
		url = url.replace("FB_CODE", code);
		return url;
	}
}
